This example mirrors additive.mesh.negative.html.

In [1]:
from pathlib import Path

from ipyniivue import download_dataset

BASE_API_URL = "https://niivue.com/demos/images"
DATA_FOLDER = Path("images")

# Download data for example
download_dataset(
    BASE_API_URL,
    DATA_FOLDER,
    files=[
        "BrainMesh_ICBM152.lh.curv",
        "yd.mz3",
        "zd.mz3",
        "BrainMesh_ICBM152.lh.mz3",
    ],
)
BrainMesh_ICBM152.lh.curv already exists.
yd.mz3 already exists.
zd.mz3 already exists.
BrainMesh_ICBM152.lh.mz3 already exists.
Dataset downloaded successfully to images.
In [2]:
import ipywidgets as widgets

import ipyniivue

# Initialize NiiVue
nv = ipyniivue.NiiVue(
    show_3d_crosshair=True,
    back_color=[0.5, 0.5, 1, 1],
    is_colorbar=True,
    slice_type=ipyniivue.SliceType.RENDER,
)

# --- Create MeshLayers ---

# Layer 1: Curvature (Index 0)
layer1 = ipyniivue.MeshLayer(
    path=DATA_FOLDER / "BrainMesh_ICBM152.lh.curv",
    colormap="gray",
    colormap_negative="",
    cal_min=0.3,
    cal_max=0.5,
    opacity=0.7,
)
layer1.colorbar_visible = False

# Layer 2: YD (Index 1) - Red/Blue
layer2 = ipyniivue.MeshLayer(
    path=DATA_FOLDER / "yd.mz3",
    colormap="red",
    colormap_negative="blue",
    use_negative_cmap=True,
    cal_min=1.5,
    cal_max=3.5,
)
# Replaces alphaThreshold: true
layer2.colormap_type = ipyniivue.ColormapType.ZERO_TO_MAX_TRANSLUCENT_BELOW_MIN
layer2.is_additive_blend = True

# Layer 3: ZD (Index 2) - Green/Blue
layer3 = ipyniivue.MeshLayer(
    path=DATA_FOLDER / "zd.mz3",
    colormap="green",
    colormap_negative="blue",
    use_negative_cmap=True,
    cal_min=1.5,
    cal_max=3.5,
)
# Replaces alphaThreshold: true
layer3.colormap_type = ipyniivue.ColormapType.ZERO_TO_MAX_TRANSLUCENT_BELOW_MIN
layer3.is_additive_blend = True

# Load Meshes
nv.load_meshes(
    [
        {
            "path": DATA_FOLDER / "BrainMesh_ICBM152.lh.mz3",
            "layers": [layer1, layer2, layer3],
        }
    ]
)

# Set initial clip plane
nv.set_clip_plane(-0.1, 270, 0)


# --- UI Controls ---

# 1. Additive Blend Checkbox
additive_check = widgets.Checkbox(value=True, description="Additive")


def on_additive_change(change):
    """Handle additive change for layers 1 and 2."""
    if not nv.meshes:
        return
    mesh_id = nv.meshes[0].id
    # Affects indices 1 (yd) and 2 (zd)
    for i in range(1, 3):
        nv.set_mesh_layer_property(mesh_id, i, "is_additive_blend", change["new"])


additive_check.observe(on_additive_change, names="value")


# 2. Opacity Slider (Range 0-10, Default 5)
opacity_slider = widgets.IntSlider(min=0, max=10, value=5, description="Opacity")


def on_opacity_change(change):
    """Handle opacity change for layers 1 and 2."""
    if not nv.meshes:
        return
    mesh_id = nv.meshes[0].id
    val = change["new"] * 0.1
    # Affects indices 1 (yd) and 2 (zd)
    for i in range(1, 3):
        nv.set_mesh_layer_property(mesh_id, i, "opacity", val)


opacity_slider.observe(on_opacity_change, names="value")


# 3. Red Slider (Anterior - Controls Layer 1 'yd')
red_slider = widgets.IntSlider(min=0, max=5, value=1, description="RedAnt")


def on_red_change(change):
    """Handle red slider change (Layer 1)."""
    if not nv.meshes:
        return
    mesh_id = nv.meshes[0].id
    val = change["new"]
    # Layer index 1
    nv.set_mesh_layer_property(mesh_id, 1, "cal_min", val + 0.5)
    nv.set_mesh_layer_property(mesh_id, 1, "cal_max", val + 2.5)


red_slider.observe(on_red_change, names="value")


# 4. Green Slider (Superior - Controls Layer 2 'zd')
green_slider = widgets.IntSlider(min=0, max=5, value=1, description="GreenSup")


def on_green_change(change):
    """Handle green slider change (Layer 2)."""
    if not nv.meshes:
        return
    mesh_id = nv.meshes[0].id
    val = change["new"]
    # Layer index 2
    nv.set_mesh_layer_property(mesh_id, 2, "cal_min", val + 0.5)
    nv.set_mesh_layer_property(mesh_id, 2, "cal_max", val + 2.5)


green_slider.observe(on_green_change, names="value")


# 5. Shader Dropdown
shader_drop = widgets.Dropdown(
    options=["Edge", "Flat", "Matcap", "Matte", "Outline", "Phong", "Toon"],
    value="Matcap",
    description="Shader",
)


def on_shader_change(change):
    """Handle shader change."""
    if not nv.meshes:
        return
    mesh_id = nv.meshes[0].id
    nv.set_mesh_shader(mesh_id, change["new"])


shader_drop.observe(on_shader_change, names="value")


# --- Initial Setup Callback ---
@nv.on_mesh_loaded
def set_initial_props(m):
    """Apply all UI values to the mesh once loaded."""
    if nv.meshes:
        on_additive_change({"new": additive_check.value})
        on_opacity_change({"new": opacity_slider.value})
        on_red_change({"new": red_slider.value})
        on_green_change({"new": green_slider.value})
        on_shader_change({"new": shader_drop.value})


# Layout
ui = widgets.VBox(
    [
        widgets.HBox([additive_check, opacity_slider]),
        widgets.HBox([red_slider, green_slider]),
        shader_drop,
        nv,
    ]
)

ui
Out[2]: